package com.skiving.cloud.file.service.impl;

import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.solon.service.impl.ServiceImpl;
import com.skiving.cloud.auth.entity.AuthUser;
import com.skiving.cloud.common.utils.PageParameter;
import com.skiving.cloud.common.utils.UuidUtil;
import com.skiving.cloud.file.entity.SysFile;
import com.skiving.cloud.file.mapper.SysFileMapper;
import com.skiving.cloud.file.service.SysFileService;
import com.skiving.cloud.file.store.FileStorageDao;
import com.skiving.cloud.file.utils.FileConstant;
import org.noear.solon.annotation.Component;
import org.noear.solon.Utils;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.DownloadedFile;
import org.noear.solon.core.handle.UploadedFile;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 附件 服务层实现。
 *
 * @author skivingcloud
 * @since 2025-03-14
 */
@Component
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile>  implements SysFileService {
    /**
     * 文件存储的实现类集合，会根据fileStore来决定最终使用哪个实现类，所以实现类bean命名必须遵循fileStore+"FileStorageDao"的原则(bean名称，非类名)
     */
    @Inject
    private Map<String, FileStorageDao> fileStorageDaoMap;
    /**
     * 文件存储方案，fs:本地文件系统、hdfs:hdfs存储、filenet:filenet存储，其他方案请自行扩展。配置在application.properties中
     */
    @Inject("${skiving.cloud.file.store:fs}")
    private String fileStore;
    /**
     * 分页查询附件
     *
     * @param parameter 查询参数
     * @return 分页查询结果
     */
    @Override
    public Page<SysFile> queryByPage(PageParameter<SysFile> parameter){
        Page<SysFile> page = new Page<>(parameter.getPage(), parameter.getPageSize());
        QueryWrapper queryWrapper = QueryWrapper.create();
        if (parameter.getParam() != null) {
            //查询条件
            if (Utils.isNotBlank(parameter.getParam().getId())) {
                queryWrapper.and(SysFile::getId).like(parameter.getParam().getId());
            }
            if (Utils.isNotBlank(parameter.getParam().getFileName())) {
                queryWrapper.and(SysFile::getFileName).like(parameter.getParam().getFileName());
            }
            if (Utils.isNotBlank(parameter.getParam().getFileType())) {
                queryWrapper.and(SysFile::getFileType).like(parameter.getParam().getFileType());
            }
            if (Utils.isNotBlank(parameter.getParam().getFilePath())) {
                queryWrapper.and(SysFile::getFilePath).like(parameter.getParam().getFilePath());
            }
            if (Utils.isNotBlank(parameter.getParam().getFileState())) {
                queryWrapper.and(SysFile::getFileState).like(parameter.getParam().getFileState());
            }
            if (Utils.isNotBlank(parameter.getParam().getBusinessId())) {
                queryWrapper.and(SysFile::getBusinessId).like(parameter.getParam().getBusinessId());
            }
            if (Utils.isNotBlank(parameter.getParam().getBelongModule())) {
                queryWrapper.and(SysFile::getBelongModule).like(parameter.getParam().getBelongModule());
            }
            if (Utils.isNotBlank(parameter.getParam().getMemo())) {
                queryWrapper.and(SysFile::getMemo).like(parameter.getParam().getMemo());
            }
            if (Utils.isNotBlank(parameter.getParam().getStoreType())) {
                queryWrapper.and(SysFile::getStoreType).like(parameter.getParam().getStoreType());
            }
            if (Utils.isNotBlank(parameter.getParam().getCreateUser())) {
                queryWrapper.and(SysFile::getCreateUser).like(parameter.getParam().getCreateUser());
            }
            if (Utils.isNotBlank(parameter.getParam().getModUser())) {
                queryWrapper.and(SysFile::getModUser).like(parameter.getParam().getModUser());
            }
        }
        return this.page(page, queryWrapper);
    }

    /**
     * 上传添加文件
     *
     * @param files        文件
     * @param businessId   业务id
     * @param user         用户
     * @param belongModule 模块
     * @return 文件id
     */
    @Override
    public List<String> saveFiles(UploadedFile[] files, String businessId, AuthUser user, String belongModule) throws Exception {
        List<String> ids = new ArrayList<>();
        LocalDateTime date = LocalDateTime.now();
        for (UploadedFile file : files) {
            // 文件名
            String fileName = file.getName();
            // 后缀
            String extension = file.getExtension();
            // 字节数
            long fileSize = file.getContentSize();
            InputStream fileInputStream = file.getContent();
            SysFile fileManagement = new SysFile();
            fileManagement.setId(UuidUtil.get32UUID());
            fileManagement.setFileName(fileName);
            fileManagement.setFileType(extension);
            fileManagement.setFileSize(((float) fileSize) / 1024);// 转成KB
            fileManagement.setFileState(FileConstant.SysFileState.NORMAL.getValue());
            fileManagement.setBusinessId(businessId);
            fileManagement.setBelongModule(belongModule);
            fileManagement.setStoreType(fileStore);
            fileManagement.setVersion(0);
            fileManagement.setCreateUser(user.getId());
            fileManagement.setCreateTime(date);
            FileStorageDao fileStorageDao = fileStorageDaoMap.get(fileStore + FileConstant.FILE_DAO_END_TEXT);
            fileStorageDao.saveFile(fileManagement, fileInputStream);
            fileManagement.setFilePath("");
            this.mapper.insert(fileManagement);
            ids.add(fileManagement.getId());
        }
        return ids;
    }

    /**
     * 下载文件
     *
     * @param fileManagement 文件
     * @param ctx            response
     */
    @Override
    public void downloadFile(SysFile fileManagement, Context ctx) throws Exception {
        FileStorageDao fileStorageDao = fileStorageDaoMap.get(fileStore + FileConstant.FILE_DAO_END_TEXT);
        InputStream fileInputStream = fileStorageDao.getFile(fileManagement);
        //输出的文件名，可以自己指定
        DownloadedFile file = new DownloadedFile("application/octet-stream;charset=UTF-8", fileInputStream, fileManagement.getFileName());
        ctx.headerAdd("Pargam", "no-cache");
        ctx.headerAdd("Cache-Control", "no-cache");
        ctx.headerAdd("Content-Disposition", "attachment;filename=" + java.net.URLEncoder.encode(fileManagement.getFileName(), StandardCharsets.UTF_8));
        ctx.outputAsFile(file);
    }

    /**
     * 返回文件字节
     *
     * @param fileManagement 文件对象
     * @return 字节流
     * @throws Exception 异常
     */
    @Override
    public byte[] getByte(SysFile fileManagement) throws Exception {
        FileStorageDao fileStorageDao = fileStorageDaoMap.get(fileStore + FileConstant.FILE_DAO_END_TEXT);
        InputStream fileInputStream = fileStorageDao.getFile(fileManagement);
        return fileInputStream.readAllBytes();
    }

    /**
     * 将文件移动至“已删除”目录
     *
     * @param asList    文件id
     * @param userId    用户
     * @param fileState 状态
     * @throws Exception 异常
     */
    @Override
    public void moveFilePath(List<String> asList, String userId, String fileState) throws Exception {
        QueryWrapper queryWrapper = QueryWrapper.create().and(SysFile::getId).in(asList);
        List<SysFile> fileManagementList = this.list(queryWrapper);
        for(SysFile f : fileManagementList){
            if(!"3".equals(f.getFileState())){
                f.setFileState(fileState);// 把文件移到待删除文件夹,
                f.setModTime(LocalDateTime.now());
                f.setModUser(userId);
                FileStorageDao fileStorageDao = fileStorageDaoMap.get(fileStore + FileConstant.FILE_DAO_END_TEXT);
                fileStorageDao.moveFilePath(f, fileState);
                this.mapper.update(f);
            }
        }
    }

    /**
     * 根据业务id查询附件信息
     *
     * @param businessId 业务id
     * @return 文件列表
     */
    @Override
    public List<SysFile> getFileListByBusId(String businessId) {
        QueryWrapper queryWrapper = QueryWrapper.create().and(SysFile::getBusinessId).eq(businessId)
                .and(SysFile::getFileState).eq(FileConstant.SysFileState.NORMAL.getValue());
        return this.list(queryWrapper);
    }

    /**
     * 根据业务id查询附件数量
     *
     * @param busIds 业务id数组
     * @return 数量
     */
    @Override
    public List<Map> getFileCountByBusId(String[] busIds) {
        QueryWrapper queryWrapper = QueryWrapper.create().select(new QueryColumn("business_id"), QueryMethods.count(SysFile::getId).as("fileCount"))
                .where(SysFile::getBusinessId).in(busIds, true)
                .and(SysFile::getFileState).eq(FileConstant.SysFileState.NORMAL.getValue())
                .groupBy(SysFile::getBusinessId);
        return this.mapper.selectListByQueryAs(queryWrapper, Map.class);
    }

    /**
     * 保存文件流
     *
     * @param inputStream  文件流
     * @param businessId   业务id
     * @param userId       用户
     * @param belongModule 模块
     * @param fileName 文件名
     * @param extension 后缀
     * @return 文件id
     */
    @Override
    public String saveFileInputStream(InputStream inputStream, String businessId, String userId, String belongModule, String fileName, String extension) throws Exception {
        // 字节数
        long fileSize = inputStream.available();
        SysFile fileManagement = new SysFile();
        fileManagement.setId(UuidUtil.get32UUID());
        fileManagement.setFileName(fileName);
        fileManagement.setFileType(extension);
        fileManagement.setFileSize(((float) fileSize) / 1024);// 转成KB
        fileManagement.setFileState(FileConstant.SysFileState.NORMAL.getValue());
        fileManagement.setBusinessId(businessId);
        fileManagement.setBelongModule(belongModule);
        fileManagement.setStoreType(fileStore);
        fileManagement.setVersion(0);
        fileManagement.setCreateUser(userId);
        fileManagement.setCreateTime(LocalDateTime.now());
        FileStorageDao fileStorageDao = fileStorageDaoMap.get(fileStore + FileConstant.FILE_DAO_END_TEXT);
        fileStorageDao.saveFile(fileManagement, inputStream);
        fileManagement.setFilePath("");
        this.mapper.insert(fileManagement);
        return fileManagement.getId();
    }
}
